#include "stdafx.h"
#include "LookAtCamera.h"
#include "Globals.h"


//Globals Variable
Matrix maRotation;
GLfloat fDeltaRot = 0;

LookAtCamera::LookAtCamera()
{
	m_fRotX = 0.0f;
	m_fRotY = 0.0f;
	m_fRotZ = 0.0f;

	m_maView.SetIdentity();
	m_maWorld.SetIdentity();
}

void LookAtCamera::Init(Vector3 vPostion, Vector3 vTarget, Vector3 vUp,
		GLfloat fFovY, GLfloat fNearPlane, GLfloat fFarPlane)
{
	m_vPosition = Vector3(vPostion);
	m_vTarget = Vector3(vTarget);
	m_vUp = Vector3(vUp);

	//Calculate Projection Matrix
	GLfloat aspect = (GLfloat)Globals::screenWidth / (GLfloat)Globals::screenHeight;
	m_maProjection.SetPerspective(fFovY, aspect, fNearPlane, fFarPlane);

	//Set flag camera move change
	// true: is changed
	// false: isn't changed
	m_bIsChanged = false;

	UpdateWVMatrix();
}

//Calculate world and view matrix after changed
void LookAtCamera::UpdateWVMatrix()
{
	Matrix maRot, maTran;
	maRot.SetIdentity();
	maTran.SetIdentity();

	///Calculate World
	//rotation matrix
	Vector3 zaxis = (m_vPosition - m_vTarget).Normalize();
	Vector3 xaxis = (m_vUp.Cross(zaxis)).Normalize();
	Vector3 yaxis = (zaxis.Cross(xaxis)).Normalize();
	maRot.m[0][0] = xaxis.x;	maRot.m[0][1] = xaxis.y;	maRot.m[0][2] = xaxis.z;	maRot.m[0][3] = 0.0f;
	maRot.m[1][0] = yaxis.x;	maRot.m[1][1] = yaxis.y;	maRot.m[1][2] = yaxis.z;	maRot.m[1][3] = 0.0f;
	maRot.m[2][0] = zaxis.x;	maRot.m[2][1] = zaxis.y;	maRot.m[2][2] = zaxis.z;	maRot.m[2][3] = 0.0f;
	maRot.m[3][0] = 0.0f;		maRot.m[3][1] = 0.0f;		maRot.m[3][2] = 0.0f;		maRot.m[3][3] = 1.0f;
	//translation matrix
	maTran.m[3][0] = m_vPosition.x;
	maTran.m[3][1] = m_vPosition.y;
	maTran.m[3][2] = m_vPosition.z;
	//World matrix
	m_maWorld = maRot * maTran;

	///Calculate View
	//Invert rotation matrix
	maRot = maRot.Transpose();
	//Invert translation matrix
	maTran.m[3][0] *= -1.0f;
	maTran.m[3][1] *= -1.0f;
	maTran.m[3][2] *= -1.0f;
	//View matrix
	m_maView = maTran * maRot;
}

//...
Matrix LookAtCamera::GetWorldMatrix()
{
	return m_maWorld;
}

Matrix LookAtCamera::GetVPMatrix()
{
	return m_maView * m_maProjection;
}

Vector3 LookAtCamera::GetPosition()
{
	return m_vPosition;
}

void LookAtCamera::SetPosition(Vector3 pos)
{
	m_vPosition = Vector3(pos);
}

//Update camera
void LookAtCamera::Update(ESContext *esContext, GLfloat deltaTime)
{
	if(m_bIsChanged)
	{
		Vector3 currentCamPos = Globals::MainCamera->GetPosition();
		Vector3 newCamPos = Vector3(currentCamPos.x, Globals::terrain->GetHeight(currentCamPos.x, currentCamPos.z) + 3, currentCamPos.z);
		
		Vector3 tangent = newCamPos - currentCamPos;
		float rotOffset = m_vTarget.Dot(tangent);

		if (rotOffset != 0)
		{
			maRotation.SetIdentity().SetRotationY(rotOffset/360.0f);
			UpdateNewTargetRotation();
			esLogMessage("Rotation: %f, Target: %f %f %f\n", rotOffset, m_vTarget.x, m_vTarget.y, m_vTarget.z);
		}
		Globals::MainCamera->SetPosition(newCamPos);
		UpdateWVMatrix();
		//printf("Camera POS: [%f, %f, %f]\n", m_vPosition.x, m_vPosition.y, m_vPosition.z);
		m_bIsChanged = false;
	}
}

///Translation
void LookAtCamera::MoveLeft(GLfloat offset)
{
	m_bIsChanged = true;	
	MoveX(-offset);
}

void LookAtCamera::MoveRight(GLfloat offset)
{
	m_bIsChanged = true;
	MoveX(offset);
}

void LookAtCamera::MoveDown(GLfloat offset)
{
	m_bIsChanged = true;
	MoveY(-offset);
}

void LookAtCamera::MoveUp(GLfloat offset)
{
	m_bIsChanged = true;
	MoveY(offset);
}

void LookAtCamera::MoveBackward(GLfloat offset)
{
	m_bIsChanged = true;
	MoveZ(offset);
}

void LookAtCamera::MoveForward(GLfloat offset)
{
	m_bIsChanged = true;
	MoveZ(-offset);
}
///Rotation
void LookAtCamera::RotateLeft(GLfloat offset)
{
	m_bIsChanged = true;
	m_fRotY = -offset;
	maRotation.SetIdentity().SetRotationY(m_fRotY);
	UpdateNewTargetRotation();
}

void LookAtCamera::RotateRight(GLfloat offset)
{
	m_bIsChanged = true;
	m_fRotY = offset;
	maRotation.SetIdentity().SetRotationY(m_fRotY);
	UpdateNewTargetRotation();
}


void LookAtCamera::RotateDown(GLfloat offset)
{
	if(fDeltaRot <= -3.14/2.0f)
	{
		m_bIsChanged = false;
		offset = 0;
	}
	else
	{
		m_bIsChanged = true;
		m_fRotX = -offset;
		maRotation.SetIdentity().SetRotationX(m_fRotX);
		fDeltaRot += m_fRotX;
		UpdateNewTargetRotation();
	}
}

void LookAtCamera::RotateUp(GLfloat offset)
{
	if(fDeltaRot >= 3.14/2.0f)
	{
		m_bIsChanged = false;
		offset = 0;
	}
	else
	{
		m_bIsChanged = true;
		m_fRotX = offset;
		maRotation.SetIdentity().SetRotationX(m_fRotX);
		fDeltaRot += m_fRotX;
		UpdateNewTargetRotation();
	}
}


///Calculate Translation
void LookAtCamera::MoveX(GLfloat offset)
{
	Vector3 deltaMoveZ = (m_vPosition - m_vTarget).Normalize();
	Vector3 deltaMoveX = (m_vUp.Cross(deltaMoveZ)).Normalize();	

	deltaMoveX *= offset;
	m_vPosition += deltaMoveX;
	m_vTarget += deltaMoveX;
}

void LookAtCamera::MoveY(GLfloat offset)
{
	Vector3 deltaMoveZ = (m_vPosition - m_vTarget).Normalize();
	Vector3 deltaMoveX = (m_vUp.Cross(deltaMoveZ)).Normalize();	
	Vector3 deltaMoveY = (deltaMoveZ.Cross(deltaMoveX)).Normalize();

	deltaMoveY *= offset;
	m_vPosition += deltaMoveY;
	m_vTarget += deltaMoveY;
}

void LookAtCamera::MoveZ(GLfloat offset)
{
	Vector3 deltaMoveZ = (m_vPosition - m_vTarget).Normalize();

	deltaMoveZ *= offset;
	m_vPosition += deltaMoveZ;
	m_vTarget += deltaMoveZ;
}

///Calculate Rotation
void LookAtCamera::UpdateNewTargetRotation()
{
	//Calculation Rotation
	Vector4 vTargetL = Vector4(0.0f, 0.0f, -(m_vPosition - m_vTarget).Length(), 1.0f);
	Vector4 vTargetNewL = vTargetL * maRotation;
	Vector4 vTargetNewW = vTargetNewL * m_maWorld;
	m_vTarget = Vector3(vTargetNewW.x, vTargetNewW.y, vTargetNewW.z);
}